不开 SELinux 的情况下,执行 podman run ... -v /local/path:/container/path ... 容器可正常挂载 volume 并使用。
在 SELinux 下,用户创建的文件或文件夹的默认 SELinux Type 为 user_home_t,而 SELinux 里面对于容器的规则会让容器无法读写 SELinux Type 为 user_home_t 的文件。
命令改成 podman run ... -v /local/path:/container/path:Z ... 则系统会将挂载点的 SELinux Type 改为 container_file_t:s0...,比如 container_file_t:s0:c191,c797, 表示这个 volume 只由一个容器独占。
也能将命令改成 podman run ... -v /local/path:/container/path:z ... 系统会将挂载点的 SELinux Type 改为 container_file_t:s0 表示这个 volume 可以给多个容器分享。
注意: -v 参数最后的大写 Z 表示这个卷由一个容器独占,小写 z 表示卷可以在多个容器间共享。
sudo firewall-cmd --reload 之后防火墙会根据配置文件重新生成流量过滤规则,但是 podman 容器的流量过滤规规则没有被 firewalld 保存在文件中,所以此时容器的规则就全部丢失了。
同时,防火墙 reload 之后,podman 并不会重新为容器添加过滤规则,这就导致了所有依赖 firewalld 的容器都断网了。podman 仅仅只是在容器启动的时候,在 firewall 中为这个容器添加了一次规则,就没有然后了。
bridge 网络模式的容器的流量转发靠的是 firewalld,而 网络模式为 host 的容器的网络流量由于不走 firewalld 转发 ,所以使用 host 网络模式的容器并不受影响。
这个问题 2020 年就出现了,这里 Github issue 也讨论了这个问题。结论是目前无解。
在为 pod container 开放端口时,不能用:
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=dns --permanent
sudo firewall-cmd --reload
应该用:
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=dns --permanent
sudo firewall-cmd --zone=public --add-service=dns
这是 bug 的表现:
[root@rhel8a zones]# sudo firewall-cmd --zone=trusted --list-all
trusted (active)
target: ACCEPT
icmp-block-inversion: no
interfaces:
sources: 10.88.0.5/32 10.88.0.6/32
services:
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[root@rhel8a zones]# podman inspect vlmcsd | grep IPA
"IPAddress": "10.88.0.6",
"IPAddress": "10.88.0.6",
"IPAMConfig": null,
[root@rhel8a zones]# sudo firewall-cmd --reload
success
[root@rhel8a zones]# sudo firewall-cmd --zone=trusted --list-all
trusted
target: ACCEPT
icmp-block-inversion: no
interfaces:
sources:
services:
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
root 用户的,网络模式为 bridge 的容器启动之后,相应端口直接就是打开的,不需要操作 firewalld。
root 用户的,网络模式为 host 的容器启动之后,需要正常打开端口。
而普通用户的容器的网络模式不论是 bridge 还是 host,容器启动之后,都需要在 firewalld 中打开对应端口。
在 Linux 上,非特权用户无法打开低于端口号 1024 的端口,这个限制同样适用于 Podman。
因此,默认情况下,rootless 容器无法暴露低于端口号 1024 的端口。
要允许所有非特权应用程序绑定到低于1024的端口,可以使用以下命令解除此限制: sysctl net.ipv4.ip_unprivileged_port_start=0
要永久解除此限制,运行: sysctl -w net.ipv4.ip_unprivileged_port_start=0
从 net.ipv4.ip_unprivileged_port_start 指定端口开始的端口都是 unprivileged port,例子里表示从端口号 0 及 0 之后的端口都是 unprivileged port,即,没有特权端口。
或者,可以用防火墙的端口转发功能,将流向特权端口的流量转发到非特权端口上。
在 rootless 容器中,podman 自动管理 ID 映射的选项是 --userns 它有如下可选值:
keep-id:uid=<uid>,gid=<gid> 将当前用户映射到容器内的 <uid>:<gid> 账户用户也可以用 --uidmap <start-number-in-container>:<start-number-in-host>:<range> 自己管理到容器的 ID 映射,用法 --uidmap 0:100000:5000 表示,将 host 上 100000 开始的 5000 个 ID 映射到容器中,并且这些 ID 在容器内从 0 开始 (100000 对应容器内 0, 100001 对应 1 ... 104999 对应 5000)。选项 --gidmap 的功能与 --uidmap 类似。它们与 --userns 冲突。
在容器内执行 cat /proc/self/{uid_map,gid_map} 可以查看容器中的 ID 映射规则,输出格式也为 <start-number-in-container>:<start-number-in-host>:<range>。
和 docker 不同, podman run --restart (unless-stopped | always) ... 之后,如果机器重启,容器会被系统停止,并且容器不随机器一同重启。
如果使用 systemd 来管理容器启停,那么 podman run ... --restart ... 参数绝对不能用。
若要让容器随机器一同重启,需要自已写一个 .service 文件放在 /etc/systemd/system 下。
.service 文件的内容可以用 podman generate systemd --name <container_name> 生成。
下面的命令可以将输出重定向到 /etc/systemd/system 下,一步到位:
podman generate systemd \
--name <container_name> \
> /etc/systemd/system/container-<container_name>.service
在命令中给定 --new 参数表示每次重启时从镜象生成新的容器,比如:
podman generate systemd --new \
--name <container_name> \
> /etc/systemd/system/container-<container_name>.service
也可以在 podman generate systemd --files --name <container_name> 生成 .service 文件后,执行 mv container-<container_name>.service /etc/systemd/system/container-<container_name>.service 把文件放在 /etc/systemd/system 下。
注意: 如果开了 SELinux,在移动 .service 文件后还要用 restorecon -v /etc/systemd/system/container-<container_name>.service 重新给文件正确的权限。
然后执行 systemctl enable --now <container_name>.service 即可让指定的容器开机启动。
只需要执行 loginctl enable-linger [USER…] 启用 / 禁止用户逗留 (相当于保持登录状态)。
如果指定了用户名或 UID,那么系统将会在启动时自动为这些用户派生出用户管理器,并且在用户登出后继续保持运行。这样就可以允许未登录的用户在后台运行持续时间很长的服务。
注意: root 用户也不例外。不配置 enable-linger 的情况下,不登陆是不会被分配用户管理器的。
如果没有指定任何参数,那么将作用于当前调用者的用户。
执行 loginctl disable-linger [USER…] 取消操作。
制作 .service 文件的方法参考系统级容器 .service 文件的生成方法。
然后放一个归属普通用户的 vlmcsd 容器的例子:
#!/bin/bash
podman run \
-d \
--name vlmcsd \
--network bridge \
-p 1688:1688 \
docker.io/mikolatero/vlmcsd:latest
# --restart unless-stopped \
############
#
# podman generate systemd --new \
# --name vlmcsd \
# > $HOME/.config/systemd/user/container-vlmcsd.service
#
############
mkdir -p $HOME/.config/systemd/user
podman generate systemd --new --files --name vlmcsd
mv container-vlmcsd.service $HOME/.config/systemd/user/container-vlmcsd.service
restorecon -v $HOME/.config/systemd/user/container-vlmcsd.service
systemctl --user enable container-vlmcsd
sudo firewall-cmd --add-port=1688/tcp --permanent
sudo firewall-cmd --add-port=1688/tcp
如果用 systemd 管理容器启停,那么 podman run ... --restart ... 这个参数绝对不能用。如果没有加这个参数, podman generate systemd 生成的 .service 文件中,服务启动时执行的命令如下:
...
ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --sdnotify=conmon --cgroups=no-conmon --rm --replace -d --name adguardhome ......
...
注意里面有一个 podman run ... --rm ... 参数,参数 --rm 和 --restart 冲突。如果在首次生成容器时的命令用 podman run ... --restart ... ,那么,由 podman generate systemd 得到的 .service 文件中的启动命令会变成 podman run ... --rm ... --restart ... 。这个命令是无法启动容器的。
sudo journalctl -n 100 可以看到日志中有类似这样的报错:
...
Error: the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"
...